//----------------------------------------------------------------------------------------------------------------------
// Copyright (c) 2012 James Whitworth
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//----------------------------------------------------------------------------------------------------------------------
#include <gmock/gmock.h>
//----------------------------------------------------------------------------------------------------------------------
#include <sqbind/sqbSquirrelObject.h>
//----------------------------------------------------------------------------------------------------------------------
#include "fixtures/TypeFixture.h"
//----------------------------------------------------------------------------------------------------------------------

typedef SquirrelFixture SquirrelObjectTest;

//----------------------------------------------------------------------------------------------------------------------
template<typename TypeParam>
class SquirrelObjectTypedTest : public BaseTypeFixture<TypeParam> {};
TYPED_TEST_CASE(SquirrelObjectTypedTest, AllTypes);

//----------------------------------------------------------------------------------------------------------------------
TEST_F(SquirrelObjectTest, TestConstructionAssignment)
{
  // test default constructor
  //
  {
    sqb::SquirrelObject object;
    EXPECT_EQ(OT_NULL, sq_type(static_cast<HSQOBJECT>(object)));
  }

  // test stack index constructor
  //
  {
    sq_newtable(m_vm);
    HSQOBJECT sqobject;
    sq_getstackobj(m_vm, -1, &sqobject);
    EXPECT_EQ(0, sq_getrefcount(m_vm, &sqobject));

    sqb::SquirrelObject object(m_vm, -1);
    EXPECT_EQ(OT_TABLE, sq_type(static_cast<HSQOBJECT>(object)));
    EXPECT_EQ(1, sq_getrefcount(m_vm, &sqobject));

    sq_poptop(m_vm);
  }

  // test HSQOBJECT constructor
  //
  {
    sq_newtable(m_vm);
    HSQOBJECT sqobject;
    sq_getstackobj(m_vm, -1, &sqobject);
    EXPECT_EQ(0, sq_getrefcount(m_vm, &sqobject));

    sqb::SquirrelObject object(m_vm, sqobject);
    EXPECT_EQ(OT_TABLE, sq_type(static_cast<HSQOBJECT>(object)));
    EXPECT_EQ(1, sq_getrefcount(m_vm, &sqobject));

    sq_poptop(m_vm);
  }

  // test copy constructor
  //
  {
    sq_newtable(m_vm);
    HSQOBJECT sqobject;
    sq_getstackobj(m_vm, -1, &sqobject);
    EXPECT_EQ(0, sq_getrefcount(m_vm, &sqobject));

    sqb::SquirrelObject object(m_vm, -1);
    EXPECT_EQ(OT_TABLE, sq_type(static_cast<HSQOBJECT>(object)));

    EXPECT_EQ(1, sq_getrefcount(m_vm, &sqobject));

    sqb::SquirrelObject copy(object);
    EXPECT_EQ(OT_TABLE, sq_type(static_cast<HSQOBJECT>(copy)));

    EXPECT_EQ(2, sq_getrefcount(m_vm, &sqobject));

    sq_poptop(m_vm);
  }

  // test assignment operator
  //
  {
    sq_newtable(m_vm);
    sq_newarray(m_vm, 0);
    HSQOBJECT sqobjects[2];
    sq_getstackobj(m_vm, -2, &sqobjects[0]);
    sq_getstackobj(m_vm, -1, &sqobjects[1]);
    EXPECT_EQ(0, sq_getrefcount(m_vm, &sqobjects[0]));
    EXPECT_EQ(0, sq_getrefcount(m_vm, &sqobjects[1]));

    sqb::SquirrelObject objects[] = 
    {
      sqb::SquirrelObject(m_vm, -2),
      sqb::SquirrelObject(m_vm, -1),
    };

    EXPECT_EQ(1, sq_getrefcount(m_vm, &sqobjects[0]));
    EXPECT_EQ(1, sq_getrefcount(m_vm, &sqobjects[1]));

    EXPECT_EQ(OT_TABLE, sq_type(static_cast<HSQOBJECT>(objects[0])));
    EXPECT_EQ(OT_ARRAY, sq_type(static_cast<HSQOBJECT>(objects[1])));

    objects[0] = objects[1];

    EXPECT_EQ(0, sq_getrefcount(m_vm, &sqobjects[0]));
    EXPECT_EQ(2, sq_getrefcount(m_vm, &sqobjects[1]));

    EXPECT_EQ(OT_ARRAY, sq_type(static_cast<HSQOBJECT>(objects[0])));

    sq_pop(m_vm, 2);
  }

  // test destructor releases reference
  //
  {
    sq_newtable(m_vm);
    HSQOBJECT sqobject;
    sq_getstackobj(m_vm, -1, &sqobject);

    EXPECT_EQ(0, sq_getrefcount(m_vm, &sqobject));
    {
      sqb::SquirrelObject object(m_vm, sqobject);
      EXPECT_EQ(1, sq_getrefcount(m_vm, &sqobject));
    }
    EXPECT_EQ(0, sq_getrefcount(m_vm, &sqobject));

    sq_poptop(m_vm);
  }
}

//----------------------------------------------------------------------------------------------------------------------
TEST_F(SquirrelObjectTest, TestReferenceRelease)
{
  sqb::SquirrelObject object;
  EXPECT_EQ(OT_NULL, sq_type(static_cast<HSQOBJECT>(object)));

  sq_newtable(m_vm);
  HSQOBJECT sqobject;
  sq_getstackobj(m_vm, -1, &sqobject);

  EXPECT_EQ(0, sq_getrefcount(m_vm, &sqobject));
  EXPECT_EQ(OT_NULL, sq_type(static_cast<HSQOBJECT>(object)));

  // test stack index referencing
  //
  object.Reference(m_vm, -1);

  EXPECT_EQ(1, sq_getrefcount(m_vm, &sqobject));
  EXPECT_EQ(OT_TABLE, sq_type(static_cast<HSQOBJECT>(object)));

  object.Release();

  EXPECT_EQ(0, sq_getrefcount(m_vm, &sqobject));
  EXPECT_EQ(OT_NULL, sq_type(static_cast<HSQOBJECT>(object)));

  // test HSQOBJECT referencing
  //
  object.Reference(m_vm, sqobject);

  EXPECT_EQ(1, sq_getrefcount(m_vm, &sqobject));
  EXPECT_EQ(OT_TABLE, sq_type(static_cast<HSQOBJECT>(object)));

  object.Release();

  EXPECT_EQ(0, sq_getrefcount(m_vm, &sqobject));
  EXPECT_EQ(OT_NULL, sq_type(static_cast<HSQOBJECT>(object)));

  // test SquirrelObject referencing
  //
  sqb::SquirrelObject other(m_vm, -1);

  object.Reference(other);

  EXPECT_EQ(2, sq_getrefcount(m_vm, &sqobject));
  EXPECT_EQ(OT_TABLE, sq_type(static_cast<HSQOBJECT>(object)));

  object.Release();

  EXPECT_EQ(1, sq_getrefcount(m_vm, &sqobject));
  EXPECT_EQ(OT_NULL, sq_type(static_cast<HSQOBJECT>(object)));

  sq_poptop(m_vm);
}

//----------------------------------------------------------------------------------------------------------------------
TEST_F(SquirrelObjectTest, TestGetType)
{
  sqb::SquirrelObject object;

  EXPECT_EQ(OT_NULL, object.GetType());

  sq_pushinteger(m_vm, 0);
  object.Reference(m_vm, -1);
  sq_poptop(m_vm);

  EXPECT_EQ(OT_INTEGER, object.GetType());

  sq_newtable(m_vm);
  object.Reference(m_vm, -1);
  sq_poptop(m_vm);

  EXPECT_EQ(OT_TABLE, object.GetType());

  object.Release();

  EXPECT_EQ(OT_NULL, object.GetType());
}
